/*
 * The basic class to animate games in java.  Provides double/active rendering, variable ups, and sleep correction, among other
 * things. Designed to be extended, and blank methods overwritten.
 * 
 * METHODS TO BE OVERWRITTEN (all private void) :
 * 
 * updateGame()
 * renderGame()
 * initializeComponents()
 * mousePress(int x, int y)
 * keyPress(int keycode)
 */


package game;

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;

public class Canvas extends JPanel implements Runnable{

	/**
	 * @param args
	 */
	protected static volatile boolean isRunning = false; //boolean to tell if the game is running
	protected static volatile boolean paused = true; //boolean to tell if the game is paused
	protected static volatile boolean gameOver = true; //tells if the game is over or not
	protected static final long serialVersionUID = 1L; //serial version
	protected static final int DELAYS_PER_YIELD = 10;
	protected Thread animator; //thread of this class
	protected int fps = 80;  //desired fps
	protected Image buffer; //buffer for painting to screen
	protected Graphics bufGraphics;
	protected int period = 1000000000/fps;
	protected static final int MAX_FRAME_SKIPS = 5;
	
	public Canvas(){
		setBackground(Color.white);
		setPreferredSize(new Dimension(400, 300));
		
		setFocusable(true); //allows JPanel to recieve actions
		requestFocus();
		readyForKeyPress();  //make sure to add in termination code
		addMouseListener(new MouseAdapter(){ //allow the game to deal with mouse presses
			@Override
			public void mousePressed(MouseEvent e){
				mousePress(e.getX(), e.getY());
			}
		});
		initializeComponents();
	}
	
	//adds the canvas to the client, then automatically starts it
	@Override
	public void addNotify(){
		super.addNotify(); //JPanel addNotify (creates peer for the canvas)
		startGame();  //begin the game
	}	
	
	private void startGame(){
		//if the game hasn't already started, start it
		if(animator==null || !isRunning){
			animator = new Thread(this);
			animator.start();
		}
	}
	
	@Override
	public void run(){
		//main animation method
		long before, after, timeDiff, sleepTime;  //used in maintaining desired fps/ups
		long overSleepTime = 0L; //amount of time machine overslept
		int delays = 0; //counts number of delays(when machine didn't sleep)
		long extraTime = 0L; //how much time the machine overused for updating/rendering
		int skips = 0;
		sleepTime = 1000L/fps;
		
		while(isRunning){  //might need to change how all of this works, allowing animation to continue while paused
			while(!gameOver){
				//this is where the menu would go
				while(!paused){
					//update/render/paint the game
					before = System.nanoTime();
					updateGame();
					renderGame();
					paintGame();
					after = System.nanoTime();
					timeDiff = after-before;
					sleepTime = (period - timeDiff) - overSleepTime;
					if(sleepTime>0){
						try{
							Thread.sleep(sleepTime-(sleepTime/1000000L));  //sleep
						}catch (InterruptedException e){}
						overSleepTime = (System.nanoTime()-after)-sleepTime; //calculate how long the machine overslept
					}else{
						extraTime -= sleepTime;
						overSleepTime = 0L;
							
						//allow other essential threads to execute
						if(++delays >= DELAYS_PER_YIELD){
							Thread.yield();
							delays = 0;
						}
					}
					//update without rendering if the whole process takes too long
					while((extraTime>=period) && (skips<=MAX_FRAME_SKIPS)){
						updateGame();
						extraTime -= period;
						skips++;
					}
					skips = 0;
				}
			}
		}
		System.exit(0);
	}

	//Updates game/character state.  Doesn't, however, write the update to a buffer
	protected void updateGame(){
		System.out.println("hi");
	}
	
	//Renders updated game state to a buffer
	protected void renderGame(){
		System.out.println("bye");
	}
	
	protected void paintGame(){
		//paints buffer to screen
		Graphics g;
		try{
			g = this.getGraphics();
			//if both can be painted
			if((g != null) && (buffer != null)){
				g.drawImage(buffer, 0, 0, null);
				System.out.println("yay!!!");
			}else{
				System.out.println("error");
			}
			g.dispose();
		}catch (Exception e){
			System.out.println("Graphics error: "+e);
		}
	}
	
	//initialize all of the game components
	protected void initializeComponents(){}
	
	protected void readyForKeyPress(){
		addKeyListener(new KeyAdapter(){//use keyboard to allow for exit
			@Override
			public void keyPressed(KeyEvent e){
				keyPress(e.getKeyCode());
			}
		});
	}
	
	//deal with the mouse press coordinates
	protected void mousePress(int x, int y){}
	
	//deal with key press codes (make sure to add in termination code)
	private void keyPress(int keycode){}
	
	public synchronized void pauseGame(){
		paused = true;
	}
	
	public synchronized void resumeGame(){
		paused = false;
	}
	
	public synchronized void goToMain(){ //?? might need to be changed
		gameOver = true;
	}
	
	public synchronized void quitGame(){
		isRunning = false;
	}
}
/**
 * TODO: 
 * add in layers/layer manager
 */
